module aru_arb (
    input logic              clk,
    input logic              rst_n,
          arb_rd_req_if.in   u_arb_rd_req_if,
          arb_wr_req_if.in   u_arb_wr_req_if,
          aru_arb_dat_if.out u_aru_arb_dat_if
);

    localparam BANK_NUM = `N0;
    localparam BANK_WIDTH = `P_ARU * 2;
    localparam ENTRY_NUM = `ARB_SIZE / (BANK_WIDTH * BANK_NUM);
    localparam ENTRY_WIDTH = $clog2(ENTRY_NUM);

    logic                              wr_vld;
    arb_addr_t                         wr_addr;
    logic      [       BANK_WIDTH-1:0] wr_data           [BANK_NUM-1:0];
    logic                              rd_vld;
    arb_addr_t                         rd_addr;
    logic      [       BANK_WIDTH-1:0] rd_data           [BANK_NUM-1:0];

    logic      [      $clog2(`N0)-1:0] rd_bank_idx_base;
    logic      [      $clog2(`N0)-1:0] wr_bank_idx_base;
    logic      [$clog2(ENTRY_NUM)-1:0] rd_entry_idx_base;
    logic      [$clog2(ENTRY_NUM)-1:0] wr_entry_idx_base;

    assign wr_addr           = u_arb_wr_req_if.addr;
    assign rd_addr           = u_arb_rd_req_if.addr;
    assign rd_bank_idx_base  = rd_addr % BANK_NUM;
    assign wr_bank_idx_base  = wr_addr % BANK_NUM;
    assign rd_entry_idx_base = rd_addr / BANK_NUM;
    assign wr_entry_idx_base = wr_addr / BANK_NUM;

    logic                                wr_en       [BANK_NUM-1:0];
    logic signed [        $clog2(`N0):0] wr_req_idx_e[BANK_NUM-1:0];
    logic        [      $clog2(`N0)-1:0] wr_req_idx  [BANK_NUM-1:0];
    logic        [$clog2(ENTRY_NUM)-1:0] wr_entry_idx[BANK_NUM-1:0];
    logic        [       BANK_WIDTH-1:0] wr_mask     [BANK_NUM-1:0];
    logic signed [        $clog2(`N0):0] rd_req_idx_e[BANK_NUM-1:0];
    logic        [      $clog2(`N0)-1:0] rd_req_idx  [BANK_NUM-1:0];
    logic        [$clog2(ENTRY_NUM)-1:0] rd_entry_idx[BANK_NUM-1:0];

    genvar bank_idx;
    generate
        // 咱们就规定好，如果用的是generate，那就别在外面声明数组
        // 声明了数组，就用always块+for循环
        for (bank_idx = 0; bank_idx < BANK_NUM; bank_idx++) begin : gen_ram
            common_ram_1r1w #(
                .ADDR_WIDTH(ENTRY_WIDTH),
                .DATA_WIDTH(BANK_WIDTH),
                .DEPTH(ENTRY_NUM)
            ) u_arb_ram (
                .clk    (clk),
                .wr_en  (wr_en[bank_idx]),
                .wr_addr(wr_entry_idx[bank_idx]),
                .wr_data(wr_data[bank_idx]),
                .wr_mask(wr_mask[bank_idx]),
                .rd_en  (u_arb_rd_req_if.vld),
                .rd_addr(rd_entry_idx[bank_idx]),
                .rd_data(rd_data[bank_idx])
            );
        end
    endgenerate

    always_comb begin
        for (int bank_idx = 0; bank_idx < BANK_NUM; bank_idx++) begin
            wr_req_idx_e[bank_idx] = {1'b0, bank_idx} - {1'b0, wr_bank_idx_base};
            wr_req_idx[bank_idx]   = wr_req_idx_e[bank_idx][$clog2(`N0)-1:0];
            wr_entry_idx[bank_idx] = wr_req_idx_e[bank_idx][$clog2(`N0)] ? wr_bank_idx_base + 'd1 : wr_entry_idx_base;
            wr_mask[bank_idx]      = u_arb_wr_req_if.msk[wr_req_idx[bank_idx]*BANK_WIDTH+:BANK_WIDTH];
            wr_data[bank_idx]      = u_arb_wr_req_if.dat[wr_req_idx[bank_idx]*BANK_WIDTH*8+:BANK_WIDTH*8];
            wr_en[bank_idx]        = u_arb_wr_req_if.vld && |wr_mask[bank_idx];
            rd_req_idx_e[bank_idx] = {1'b0, bank_idx} + {1'b0, rd_bank_idx_base};
            rd_req_idx[bank_idx]   = rd_req_idx_e[bank_idx][$clog2(`N0)-1:0];
            rd_entry_idx[bank_idx] = rd_req_idx_e[bank_idx][$clog2(`N0)] ? rd_bank_idx_base + 'd1 : rd_entry_idx_base;
        end
    end

    always_comb begin
        for (int bank_idx = 0; bank_idx < BANK_NUM; bank_idx++) begin
            for (int elem = 0; elem < `P_ARU; elem++) begin
                u_aru_arb_dat_if.dat.dat[bank_idx*`P_ARU+elem] = rd_data[rd_req_idx[bank_idx]][elem*16+:16];
            end
        end
    end

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            u_aru_arb_dat_if.vld <= 1'b0;
        end else begin
            u_aru_arb_dat_if.vld <= u_arb_rd_req_if.vld;
        end
    end
endmodule
